#pragma once

#include "base.h"
#include "../container/robuf.h"
#include "../updater/s2_cov.h"

namespace mdtx::online::indicator
{
    /**
     * @brief Rolling covariance
     *
     * @tparam T scala type
     */
    template <typename T>
    using cov = detail::indi_wrap_ro<updater::covxy<T>>;

    /**
     * @brief Cumulative covariance
     *
     * @tparam T scala type
     */
    template <typename T>
    using cov_cu = detail::indi_wrap_cu<updater::covxy<T>>;

    /**
     * @brief Rolling covariance, exp weighted
     *
     * @tparam T scala type
     */
    template <typename T>
    using cov_ew = detail::indi_wrap_cu<updater::covxy_ew<T>>;

    /**
     * @brief Rolling correlation
     *
     * @tparam T scala type
     */
    template <typename T>
    using corr = detail::indi_wrap_ro<updater::corrxy<T>>;

    /**
     * @brief Cumulative correlation
     *
     * @tparam T scala type
     */
    template <typename T>
    using corr_cu = detail::indi_wrap_cu<updater::corrxy<T>>;

    /**
     * @brief Rolling correlation, exp weighted
     *
     * @tparam T scala type
     */
    template <typename T>
    using corr_ew = detail::indi_wrap_cu<updater::corrxy_ew<T>>;

    /**
     * @brief Rolling beta
     *
     * @tparam T scala type
     */
    template <typename T>
    using beta = detail::indi_wrap_ro<updater::beta<T>>;

    /**
     * @brief Cumulative beta
     *
     * @tparam T scala type
     */
    template <typename T>
    using beta_cu = detail::indi_wrap_cu<updater::beta<T>>;

    /**
     * @brief Rolling Spearman correlation
     *
     * @tparam T scala type
     */
    template <typename T>
    class corr_spearman : public indi_binary1<T>
    {
        using base = indi_binary1<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::update;

    protected:
        container::ranked_robuf<T> sx, sy;
        T r;

    public:
        explicit corr_spearman(ptrdiff_t period)
            : sx(period), sy(period),
              r(T(6) / ((period * period - 1) * period))
        {
        }

        tuple_out update(tuple_in const &in) override
        {
            sx.insert(std::get<0>(in));
            sy.insert(std::get<1>(in));

            T val{};
            if (sx.full())
            {
                ptrdiff_t d2{};
                for (size_t i = 0; i < sx.rank.size(); ++i)
                {
                    T d = sx.rank[i] - sy.rank[i];
                    d2 += d * d;
                }
                val = 1 - r * d2;
            }

            return tuple_out(val);
        }
    };
}
